package ModbusTCP

import (
	"gitee.com/sevpinna/Aweto"
	"github.com/pkg/errors"
	"log"
	"net"
	"strconv"
	"strings"
	"time"
)

func send(conn net.Conn, data []byte) (v []byte, err error) {
	err = conn.SetDeadline(time.Now().Add(1000 * time.Millisecond))
	if err != nil {
		log.Print(err)
		return nil, err
	}
	_, err = conn.Write(data)
	if err != nil {
		log.Print(err)
		return nil, err
	}
	var n int
	v = make([]byte, 20)
	err = conn.SetDeadline(time.Now().Add(2000 * time.Millisecond))
	if err != nil {
		log.Print(err)
		return nil, err
	}
	n, err = conn.Read(v)
	if err != nil {
		log.Print(err)
		return nil, err
	}
	return v[:n], nil
}
func (t *tag) read(conn net.Conn) (err error) {
	var v []byte
	v, err = send(conn, t.Read)
	if err != nil {
		return err
	}
	if len(v) < 9 {
		log.Print("Rev bytes short")
		return nil
	}
	if v[7] != t.Read[7] {
		log.Print(string(v[8]))
		return nil
	}
	_, _, l := getlen(t.Type)
	var swap = 2
	if t.Swap {
		swap = 0
	}
	d, err := Aweto.ToValue(v[len(v)-int(l):], t.Type, true, swap)
	if err != nil {
		errString := err.Error()
		if !strings.Contains(errString, "Data is not dword") {
			log.Print(err)
			return nil
		}
		log.Print(err)
		err = nil
	}
	t.Value = d
	dd, ok := d.(uint16)
	if t.Bit > -1 && ok {
		x := uint16(1) << t.Bit
		if dd&x > 0 {
			d = true
		} else {
			d = false
		}
	}
	callback(t.TagID, d, time.Now().Unix())
	return nil
}
func (t *tag) write(conn net.Conn, data interface{}) (err error) {
	if t.Write == nil {
		return nil
	}
	if t.Value == nil {
		return nil
	}
	var swap = 2
	if t.Swap {
		swap = 0
	}
	d, ok := data.(uint16)
	if t.Bit > -1 && ok {
		x := uint16(1) << t.Bit
		if d == 1 {
			data = t.Value.(uint16) | x
		} else {
			x = ^x
			data = t.Value.(uint16) & x
		}
	}
	var db []byte
	d2, ok2 := data.(bool)
	if t.Type == "bool" && ok2 {
		if d2 {
			db, err = Aweto.ToBytes(uint16(65280), true, swap, 0)
		} else {
			db, err = Aweto.ToBytes(uint16(0), true, swap, 0)
		}
	} else {
		db, err = Aweto.ToBytes(data, true, swap, 0)
	}
	if err != nil {
		errString := err.Error()
		if !strings.Contains(errString, "Data is not") {
			log.Print(err)
			return nil
		}
		log.Print(err)
	}
	b := Aweto.BytesCombine(t.Write, db)
	var v []byte
	v, err = send(conn, b)
	if len(v) < 9 {
		log.Print("Rev Data len is not")
		return nil
	}
	if len(v) == 12 {
		return nil
	}
	log.Print("Write fail")
	return nil
}
func (p *protocol) getRead(Type string) []byte {
	index, _ := Aweto.ToBytes(uint16(1), true, 0, 0)
	mode := []byte{0, 0}
	id := []byte{uint8(p.ID)}
	f, _ := getfunc(p.Register, false)
	addr, _ := Aweto.ToBytes(uint16(p.Addr), true, 0, 0)
	wordlen, _, _ := getlen(Type)
	data := Aweto.BytesCombine(id, f, addr, wordlen)
	datalen, _ := Aweto.ToBytes(uint16(len(data)), true, 0, 0)
	return Aweto.BytesCombine(index, mode, datalen, data)
}
func (p *protocol) getWrite(Type string) []byte {
	index, _ := Aweto.ToBytes(uint16(2), true, 0, 0)
	mode := []byte{0, 0}
	id := []byte{uint8(p.ID)}
	f, err := getfunc(p.Register, true)
	if err != nil {
		return nil
	}
	addr, _ := Aweto.ToBytes(uint16(p.Addr), true, 0, 0)
	wordlen, byteslen, bytesnum := getlen(Type)
	var data []byte
	if f[0] == 5 {
		data = Aweto.BytesCombine(id, f, addr)
	} else {
		data = Aweto.BytesCombine(id, f, addr, wordlen, byteslen)
	}
	datalen, _ := Aweto.ToBytes(uint16(len(data))+bytesnum, true, 0, 0)
	return Aweto.BytesCombine(index, mode, datalen, data)
}
func getfunc(Reg string, rw bool) (f []byte, err error) {
	reg, _ := strconv.ParseInt(Reg, 0, 0)
	switch reg {
	case 1:
		if rw {
			f = []byte{5}
		} else {
			f = []byte{1}
		}
	case 2:
		if rw {
			err = errors.New("Can not Write to Input status!")
		} else {
			f = []byte{2}
		}
	case 4:
		if rw {
			err = errors.New("Can not Write to Input register!")
		} else {
			f = []byte{4}
		}
	case 3:
		if rw {
			f = []byte{16}
		} else {
			f = []byte{3}
		}
	}
	return
}
func getlen(t string) (wordlen, byteslen []byte, byteslennum uint16) {
	switch t {
	case "bool", "uint16", "int16":
		wordlen = []byte{0, 1}
		byteslen = []byte{2}
		byteslennum = 2
	case "uint32", "int32", "float32":
		wordlen = []byte{0, 2}
		byteslen = []byte{4}
		byteslennum = 4
	case "float64":
		wordlen = []byte{0, 4}
		byteslen = []byte{8}
		byteslennum = 8
	}
	return
}
